package no.hig.ekm.ircclient;

import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.Vector;

import jerklib.ConnectionManager;
import jerklib.Profile;
import jerklib.Session;

/**
 * Class intended for management of all the connections etc. It is responsible for 
 * keeping track of connections to servers etc.
 * 
 * @author morten, eigil
 */
public class ConnectionManagement {
	//Internationalization
	private static ResourceBundle messages;
	
	//Sessions
	private HashMap<String, Session> sessions;
	private Vector<Session> invisible;
	
	//IRC-references
	private ConnectionManager manager;		//The main connection manager
	private Profile defaultProfile;			//The default IRC-profile
	private ChannelWindow serverMessages;	//The GUI-window used to display serverMessages.
	
	/**
	 * Initializes the datastructures used to keep track of IRC-sessions.
	 */
	ConnectionManagement() {
		manager = new ConnectionManager(new Profile("NoNameSet"));
		sessions = new HashMap<String, Session>();
		invisible = new Vector<Session>();
	}
	
	/**
	 * Creates a connection to an IRC-server, and stores it for later use.
	 * 
	 * @param server_address - The ip-address or hostname of the server, as a String
	 * @param port - A list of ports, as a string. ie: "6770-6777,6780"
	 * @param p - A reference to a IRC-Profile
	 * @param invisible - Boolean - Log on invisible or not?
	 */
	public void connectToServer(String server_address, String port, Profile p, boolean invisible) {
		Session s;
		Vector<Integer> ports = this.parsePorts(port);
		defaultProfile = p;
		
		//Request a new connection to a server
		s = manager.requestConnection(server_address, ports.elementAt(0).intValue(), defaultProfile);
		//Put a IRCEventListener to the session, to catch incoming events.
		s.addIRCEventListener(new ServerEventListener());
		
		//Store  a reference to the session, into the servers hashmap.
		sessions.put(server_address, s);
		if(invisible)
			this.invisible.add(s);
	}
	
	/**
	 * Method to check if a session is marked to be invisible. Used to check if an +i
	 * mode is supposed to be sent after connection complete.
	 * 
	 * @param s - The session to check
	 * @return true if it shall be invisible, false if not.
	 */
	public boolean isInvisible(Session s) {
		return invisible.contains(s);
	}
	
	/**
	 * Method used to make shure that the session is stored, with its real hostname.
	 * 
	 * @param s - Session object, referencing to a connected session.
	 */
	public void joinComplete(Session s) {
		sessions.put(s.getConnectedHostName(), s);
	}
	
	/**
	 * Method to join a channel at a server. If you try to join a channel at a server where
	 * we haven't connected, an IRCClientException is thrown.
	 * 
	 * @param serverName - A hostname as a string.
	 * @param channelName - The channelname, passed as a string.
	 * @throws IRCClientException - If we dont already have a session to the hostname.
	 */
	public void joinChannel(String serverName, String channelName) throws IRCClientException {
		Session s;
		
		//If we are not able to find a server-session:
		if((s = sessions.get(serverName)) == null) {
			//Throw an exception
			throw new IRCClientException("Cant join channel at non-existing server");
		} else {
			//If we find a server-session; join the channel!
			s.join(channelName);			
		}
	}
	
	/**
	 * Method to join a channel at a server. If you try to join a channel at a server where
	 * we haven't connected, an IRCClientException is thrown.
	 * 
	 * @param serverName - A hostname as a string.
	 * @param channelName - The channelname, passed as a string.
	 * @param password - ChannelPassword, as a string
	 * @throws IRCClientException - If we dont already have a session to the hostname.
	 */
	public void joinChannel(String serverName, String channelName, String password) throws IRCClientException {
		Session s;
		
		//If we are not able to find a server-session:
		if((s = sessions.get(serverName)) == null) {
			//Throw an exception
			throw new IRCClientException("Cant join channel at non-existing server");
		} else {
			//If we find a server-session; join the channel!
			s.join(channelName, password);			
		}
	}
	
	/**
	 * Method wich sends a raw message to the server. Throws an IRCClientException if we
	 * dont alreadty have a connection to the listed server.
	 * 
	 * @param serverName -  A hostname as a string.
	 * @param message - The message as a string.
	 * @throws IRCClientException - If we dont have an active connection to the server.
	 */
	public void sendServerMessage(String serverName, String message) throws IRCClientException {
		Session s;
		if((s = sessions.get(serverName)) == null) {
			throw new IRCClientException("Cant join channel at non-existing server");
		} else {
			s.sayRaw(message);
		}
	}
	
	/**
	 * @return ConnectionManager - the main connection manager.
	 */
	public ConnectionManager getConnectionManager() {
		return manager;
	}
	
	/**
	 * Method which parses a string of ports, and returns the result as a Vector of Integers.
	 * 
	 * @param p - Portname as a string
	 * @return Vectos<Integer> - A vector with all the port as Integers.
	 */
	public Vector<Integer> parsePorts(String p) {
		char tmp;
		boolean range = false;
		StringBuilder digit = new StringBuilder("");
		Vector<Integer> ports = new Vector<Integer>();
		
		for(int i = 0; i <= p.length(); i++) {
			if(p.length() == i)	tmp = ',';
			else tmp = p.charAt(i);
			
			if(tmp == '-') {
				range = true;
				ports.add(new Integer(Integer.parseInt(digit.toString())));
			}
			if(tmp == ',' && range) {
				for(int j = ports.lastElement().intValue()+1; j <= Integer.parseInt(digit.toString()); j++) {
					ports.add(new Integer(j));
				}
				range = false;
			} else if (tmp == ',' && !range) {
				ports.add(new Integer(Integer.parseInt(digit.toString())));
			}
			if(tmp >= '0' && tmp <= '9'){
				digit.append(tmp);
			} else {
				digit = new StringBuilder("");
			}
		}

		return ports;
	}
	
	/**
	 * Method used to return a Session object, based on the hostname.
	 * 
	 * @param servername - The hostname of the server
	 * @return - A sessionobject, or null if no server is found.
	 */
	public Session getSession(String servername) {
		return sessions.get(servername);
	}
	
	/**
	 * Sets the ResourceBundle used for internationalization
	 * 
	 * @param m
	 */
	public static void setMessages(ResourceBundle m) {
		messages = m;
	}
}